package com.yuanchu.store.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuanchu.common.exception.YuanChuException;
import com.yuanchu.common.utils.DateFormatUtils;
import com.yuanchu.common.utils.PageResult;
import com.yuanchu.store.client.FeignMediaClient;
import com.yuanchu.store.dao.YoshopUserDao;
import com.yuanchu.store.dto.*;
import com.yuanchu.store.entity.*;
import com.yuanchu.store.service.*;
import com.yuanchu.store.utils.RedisUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.yuanchu.common.utils.DateFormatUtils.convertStringToSeconds;


@Service("yoshopUserService")
public class YoshopUserServiceImpl extends ServiceImpl<YoshopUserDao, YoshopUserEntity> implements YoshopUserService {

    @Resource
    private YoshopUserOauthService userOauthService;
    @Resource
    private YoshopUserGradeService userGradeService;
    @Resource
    private YoshopUserCouponService userCouponService;
    @Resource
    private YoshopUserBalanceLogService userBalanceLogService;
    @Resource
    private YoshopUserPointsLogService userPointsLogService;
    @Resource
    private FeignMediaClient feignMediaClient;
    @Resource
    private YoshopUserGradeLogService userGradeLogService;
    @Resource
    private RedisUtils redisUtils;

    @Override
    public Map<String, Object> queryNewUserByday() {
        // 获取当前日期
        LocalDate today = LocalDate.now();
        // 当天开始时间（0点0分0秒）
        LocalDateTime startOfDay = today.atStartOfDay();
        long startTimestampInSeconds = startOfDay.toEpochSecond(ZoneOffset.UTC);
        // 当天结束时间（23点59分59秒）
        LocalDateTime endOfDay = today.atTime(23, 59, 59);
        long endTimestampInSeconds = endOfDay.toEpochSecond(ZoneOffset.UTC);
        long tda = getUserList(startTimestampInSeconds, endTimestampInSeconds);
        long yda = this.queryNewUserByYesterday();
        Map<String, Object> map = new HashMap<>();
        map.put("ytd", String.valueOf(yda));
        map.put("tday", String.valueOf(tda));
        return map;
    }

    public long getUserList(long startSeconds, long endSeconds) {
        LambdaQueryWrapper<YoshopUserEntity> lqw = new LambdaQueryWrapper<>();
        lqw.between(YoshopUserEntity::getCreateTime, startSeconds, endSeconds);
        return this.count(lqw);
    }

    /**
     * 查询所有会员用户id
     * @return
     */
    @Override
    public List<Integer> queryUserIds() {
        LambdaQueryWrapper<YoshopUserEntity> lqw = new LambdaQueryWrapper<>();
        lqw.eq(YoshopUserEntity::getStoreId, 10001);
        lqw.eq(YoshopUserEntity::getIsDelete, 0);
        List<YoshopUserEntity> users = this.list(lqw);
        return users.stream().filter(user -> user.getGradeId() != 0)
                .map(YoshopUserEntity::getUserId).collect(Collectors.toList());
    }

    @Override
    public long queryNewUserByYesterday() {
        // 获取当前日期前一天的日期
        LocalDate yesterday = LocalDate.now().minus(1, ChronoUnit.DAYS);
        // 计算昨天开始时间（0点0分0秒）的时间戳
        LocalDateTime startOfYesterday = yesterday.atStartOfDay();
        long yesterdayStartInSeconds = startOfYesterday.toEpochSecond(ZoneOffset.UTC);
        // 计算昨天结束时间（23点59分59秒）的时间戳
        LocalDateTime endOfYesterday = yesterday.plusDays(1).atStartOfDay().minusSeconds(1);
        long yesterdayEndInSeconds = endOfYesterday.toEpochSecond(ZoneOffset.UTC);

        return getUserList(yesterdayStartInSeconds, yesterdayEndInSeconds);
    }

    @Override
    public YoshopUserEntity getUserInfo(Integer userId) {
        // 查询缓存
        String cacheKey = "user:info:" + userId;
        if (redisUtils.hasKey(cacheKey)) {
            return (YoshopUserEntity) redisUtils.get(cacheKey);
        }

        YoshopUserEntity user = this.getById(userId);
        // 获取用户绑定的第三方登录信息
        YoshopUserOauthEntity userOauth = userOauthService.getOne(new LambdaQueryWrapper<YoshopUserOauthEntity>().eq(YoshopUserOauthEntity::getUserId, userId));
        user.setCurrentOauth(userOauth);
        // 获取用户等级信息
        UserGradeDto grade = userGradeService.getUserGradeInfo(user.getGradeId());
        user.setGrade(grade);
        user.setAvatarUrl(feignMediaClient.queryFiles(user.getAvatarId()).getPreviewUrl());
        // 设置缓存
        redisUtils.set(cacheKey, user, 60 * 60 * 24);

        return user;
    }

    @Override
    public UserAssetDto getUserAsset(Integer userId) {
        // String cacheKey = "user:asset:" + userId;
        // if (redisUtils.hasKey(cacheKey)) {
        //     return (UserAssetDto) redisUtils.get(cacheKey);
        // }

        YoshopUserEntity user = this.getById(userId);
        if (user == null) {
            return null;
        }
        UserAssetDto userAsset = new UserAssetDto();
        userAsset.setBalance(String.valueOf(user.getBalance()));
        userAsset.setPoints(user.getPoints());
        int couponCount = userCouponService.count(
                new LambdaQueryWrapper<YoshopUserCouponEntity>()
                        .eq(YoshopUserCouponEntity::getUserId, userId)
                        .eq(YoshopUserCouponEntity::getIsUse, 0)
                        .eq(YoshopUserCouponEntity::getIsExpire, 0));

        userAsset.setCoupon(couponCount);

        // 设置缓存
        // redisUtils.set(cacheKey, userAsset, 60 * 60 * 24);

        return userAsset;
    }

    @Override
    @Transactional
    public boolean decrUserBalance(OrderMessage message) {
        YoshopUserEntity user = this.getById(message.getUserId());
        user.setUserId(message.getUserId());
        String payPrice = message.getPayPrice();
        user.setBalance(String.valueOf(new BigDecimal(user.getBalance()).subtract(new BigDecimal(payPrice))));
        user.setPayMoney(String.valueOf(new BigDecimal(user.getPayMoney()).add(new BigDecimal(payPrice))));
        // user.setPoints(user.getPoints() + message.getPointsBonus());
        user.setUpdateTime(String.valueOf(Instant.now().toEpochMilli()/1000));
        boolean update = this.updateById(user);
        if (!update){
            throw new YuanChuException("更新用户余额失败");
        }
        // 删除缓存
        redisUtils.del("user:info:" + message.getUserId());
        return true;
    }

    @Override
    @Transactional
    public boolean deductUserBalance(Integer userId, String payMoney, Integer points, Integer storeId) {
        // YoshopUserEntity userEntity = this.getById(userId);
        YoshopUserEntity userEntity = this.getOne(new LambdaQueryWrapper<YoshopUserEntity>()
                .eq(YoshopUserEntity::getUserId, userId).eq(YoshopUserEntity::getStoreId, storeId));
        userEntity.setBalance(String.valueOf(new BigDecimal(userEntity.getBalance()).subtract(new BigDecimal(payMoney))));
        userEntity.setPayMoney(String.valueOf(new BigDecimal(userEntity.getPayMoney()).add(new BigDecimal(payMoney))));
        userEntity.setPoints(userEntity.getPoints() - points);
        userEntity.setUpdateTime(String.valueOf(Instant.now().toEpochMilli()/1000));
        boolean update = this.updateById(userEntity);
        if (!update){
            throw new YuanChuException("更新用户余额失败");
        }
        // 删除缓存
        redisUtils.del("user:info:" + userId);
        return true;
    }

    @Override
    @Transactional
    public boolean updateUserPoints(Integer userId, Integer points, Integer storeId) {
        YoshopUserEntity userEntity = this.getOne(new LambdaQueryWrapper<YoshopUserEntity>()
                .eq(YoshopUserEntity::getUserId, userId).eq(YoshopUserEntity::getStoreId, storeId));
        userEntity.setPoints(userEntity.getPoints() + points);
        userEntity.setUpdateTime(String.valueOf(Instant.now().toEpochMilli()/1000));
        boolean update = this.updateById(userEntity);
        if (!update){
            throw new YuanChuException("更新用户积分失败");
        }
        // 删除缓存
        redisUtils.del("user:info:" + userId);
        return true;
    }

    @Override
    public boolean updateUserPersonal(Integer userId, Integer avatarId, String nickName) {
        UpdateWrapper<YoshopUserEntity> updateWrapper = new UpdateWrapper<YoshopUserEntity>()
                .eq("user_id", userId)
                .set("nick_name", nickName)
                .set("avatar_id", avatarId)
                .set("update_time", String.valueOf(Instant.now().toEpochMilli() / 1000));
        boolean update = this.update(updateWrapper);
        if (!update){
            throw new YuanChuException("更新用户信息失败");
        }
        // 删除缓存
        redisUtils.del("user:info:" + userId);
        return true;
    }

    @Override
    public YoshopUserEntity queryUserByUserId(Integer userId) {
        YoshopUserEntity userEntity = this.getById(userId);
        YoshopUploadFileEntity file = feignMediaClient.queryFiles(userEntity.getAvatarId());
        userEntity.setAvatarUrl(file.getPreviewUrl());
        return userEntity;
    }

    @Override
    public PageResult<YoshopUserEntity> queryUserList(QueryUserParamsDto param) {
        LambdaQueryWrapper<YoshopUserEntity> lqw = new LambdaQueryWrapper<>();
        lqw.eq(param.getGradeId() != null && param.getGradeId() > 0, YoshopUserEntity::getGradeId, param.getGradeId());
        lqw.like(param.getSearch() != null, YoshopUserEntity::getStoreId, param.getSearch());
        String betweenTimeStart = param.getBetweenTimeStart();
        String betweenTimeEnd = param.getBetweenTimeEnd();
        if (StringUtils.isNoneEmpty(betweenTimeStart) && StringUtils.isNoneEmpty(betweenTimeEnd)){
            long start = convertStringToSeconds(betweenTimeStart);
            long end = convertStringToSeconds(betweenTimeEnd);
            lqw.between(YoshopUserEntity::getCreateTime, start,  end);
        }
        Page<YoshopUserEntity> userList = this.page(new Page<>(param.getPage(), param.getPageSize()), lqw);
        List<YoshopUserEntity> userGradeEntities = userList.getRecords().stream().map(user -> {
            UserGradeDto userGradeInfo = userGradeService.getUserGradeInfo(user.getGradeId());
            user.setGrade(userGradeInfo);
            YoshopUploadFileEntity file = feignMediaClient.queryFiles(user.getAvatarId());
            user.setAvatarUrl(file.getPreviewUrl());
            user.setCreateTime(DateFormatUtils.formatDatetime(user.getCreateTime()));
            return user;
        }).collect(Collectors.toList());
        return PageResult.formatPageResult(userGradeEntities, param.getPage(), param.getPageSize(), userList.getTotal());

    }

    @Override
    public void editUserBalance(EditUserMoneyDto param) {
        Integer userId = param.getUserId();
        String target = param.getTarget();
        if ("balance".equals(target)){
            // 操作金额
            String remark = param.getForm().getBalance().getRemark();
            String mode = param.getForm().getBalance().getMode();
            String money = param.getForm().getBalance().getMoney();
            eidtUserBalance(userId, mode, remark, money);
            // 删除缓存
            redisUtils.del("user:info:" + userId);
        }else if ("points".equals(target)){
            // 操作积分
            String mode = param.getForm().getPoints().getMode();
            String remark = param.getForm().getPoints().getRemark();
            String value = param.getForm().getPoints().getValue();
            edtUserPoints(userId, mode, remark, value);
            // 删除缓存
            redisUtils.del("user:info:" + userId);
        }
    }

    @Override
    public void editUserGrade(EditUserParamsDto param) {
        Integer userId = param.getUserId();
        YoshopUserEntity userEntity = this.getById(userId);
        YoshopUserGradeLogEntity userGradeLog = new YoshopUserGradeLogEntity();
        userGradeLog.setOldGradeId(userEntity.getGradeId());

        userEntity.setGradeId(param.getForm().getGradeId());
        userEntity.setUpdateTime(String.valueOf(Instant.now().toEpochMilli()/1000));
        boolean update = this.updateById(userEntity);
        if (!update){
            throw new RuntimeException("修改用户等级失败");
        }
        userGradeLog.setUserId(userId);
        userGradeLog.setRemark(param.getForm().getRemark());
        userGradeLog.setNewGradeId(param.getForm().getGradeId());
        userGradeLog.setChangeType(10);
        userGradeLog.setStoreId(userEntity.getStoreId());
        userGradeLog.setCreateTime(String.valueOf(Instant.now().toEpochMilli()/1000));
        boolean save = userGradeLogService.save(userGradeLog);
        if (!save){
            throw new RuntimeException("修改用户等级失败");
        }
        // 删除缓存
        redisUtils.del("user:info:" + userId);
    }

    @Override
    public void deleteUserGrade(Integer userId) {
        boolean update = this.update(new UpdateWrapper<YoshopUserEntity>().eq("user_id", userId).set("is_delete", 1));
        if (!update){
            throw new RuntimeException("删除用户失败");
        }
        // 删除缓存
        redisUtils.del("user:info:" + userId);
    }

    private void edtUserPoints(Integer userId, String mode, String remark, String value) {
        YoshopUserPointsLogEntity userPointsLog = new YoshopUserPointsLogEntity();
        YoshopUserEntity userEntity = this.getById(userId);
        if ("inc".equals(mode)){
            // 增加
            userEntity.setPoints(userEntity.getPoints() + Integer.parseInt(value));
            userPointsLog.setValue(value);
        }else if ("dec".equals(mode)){
            // 减少
           userEntity.setPoints(userEntity.getPoints() - Integer.parseInt(value));
           userPointsLog.setValue("-" + value);
        }else if ("final".equals(mode)){
            // 设置最终金额
            userEntity.setPoints(Integer.parseInt(value));
            userPointsLog.setValue(value);
        }
        userEntity.setUpdateTime(String.valueOf(Instant.now().toEpochMilli()/1000));
        boolean update = this.updateById(userEntity);
        if (!update){
            throw new YuanChuException("更新用户余额失败");
        }
        // 更新记录日志
        userPointsLog.setUserId(userId);
        // (10用户充值 20用户消费 30管理员操作 40订单退款)
        userPointsLog.setDescribe("后台管理员 [admin] 操作");
        userPointsLog.setRemark(remark);
        userPointsLog.setStoreId(10001);
        userPointsLog.setCreateTime(String.valueOf(Instant.now().toEpochMilli() / 1000));
        boolean save = userPointsLogService.save(userPointsLog);
        if (!save){
            throw new YuanChuException("更新用户积分日志失败");
        }
    }

    private void eidtUserBalance(Integer userId, String mode, String remark, String money) {
        YoshopUserBalanceLogEntity userBalanceLog = new YoshopUserBalanceLogEntity();
        YoshopUserEntity userEntity = this.getById(userId);
        if ("inc".equals(mode)){
            // 增加
            userEntity.setBalance(String.valueOf(new BigDecimal(userEntity.getBalance()).add(new BigDecimal(money))));
            userBalanceLog.setMoney(money);
        }else if ("dec".equals(mode)){
            // 减少
            userEntity.setBalance(String.valueOf(new BigDecimal(userEntity.getBalance()).subtract(new BigDecimal(money))));
            userBalanceLog.setMoney("-"+ money);
        }else if ("final".equals(mode)){
            // 设置最终金额
            userEntity.setBalance(money);
            userBalanceLog.setMoney(money);
        }
        userEntity.setUpdateTime(String.valueOf(Instant.now().toEpochMilli()/1000));
        boolean update = this.updateById(userEntity);
        if (!update){
            throw new YuanChuException("更新用户余额失败");
        }
        // 更新记录日志
        userBalanceLog.setUserId(userId);
        // (10用户充值 20用户消费 30管理员操作 40订单退款)
        userBalanceLog.setDescribe("后台管理员 [admin] 操作");
        userBalanceLog.setRemark(remark);
        userBalanceLog.setStoreId(10001);
        userBalanceLog.setCreateTime(String.valueOf(Instant.now().toEpochMilli() / 1000));
        boolean save = userBalanceLogService.save(userBalanceLog);
        if (!save){
            throw new YuanChuException("更新用户余额日志失败");
        }
    }


}